#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <libgen.h>
#include <fcntl.h>
#include <stdbool.h>
#include <string.h>
#include "anet.h"

int anet_tcp_listen(const char* ip,int port,int backlog)
{
    // 1.创建套接字
    int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(sock_fd == -1)
    {
        printf("Error in %s(), socket: %s\n", __func__, strerror(errno));
        return -1;
    }
 
    // 2.初始化服务器地址
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    if(inet_pton(AF_INET, ip, (void*)&server_addr.sin_addr.s_addr) == -1)
    {
        printf("Error in %s(), inet_pton: %s\n", __func__, strerror(errno));
        return -1;
    }
    server_addr.sin_port = htons(port);
 
     _anet_set_reuse_addr(sock_fd);
    // 3.绑定服务器地址
    if(bind(sock_fd, (const struct sockaddr*)&server_addr, sizeof(server_addr)) == -1)
    {
        printf("Error in %s(), bind: %s\n", __func__, strerror(errno));
        return -1;
    }
 
    // 3.监听
    if(listen(sock_fd, backlog) == -1)
    {
        printf("Error in %s(), listen: %s\n", __func__, strerror(errno));
        return -1;
    }
 
    printf("Listen start [%s:%d]...\n", inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port));
    
    return sock_fd;
}


int anet_tcp_accept(int fd,char* ip,int* port)
{
    struct sockaddr_in cli_addr;
    memset(&cli_addr, 0 , sizeof(cli_addr));
    socklen_t len = sizeof(cli_addr);
    int cli_fd = accept(fd, (struct sockaddr*)&cli_addr, &len);
    if(cli_fd == -1)
    {
        //fd耗尽了,太多的客户端同时在线，还没有释放。
        //所以服务端增加超时机制及时关掉超时的客户端，回收fd是尤其必要的
        if(errno==EMFILE)
            printf("Error in %s(), accept: %s\n", __func__, strerror(errno));
        return -1;
    }
     printf("New connect: [%s:%d] [client fd=%d]\n", \
        inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port),cli_fd);
    if(ip!=NULL)
        sprintf(ip,"%s",inet_ntoa(cli_addr.sin_addr));
    if(port!=NULL)
        *port = ntohs(cli_addr.sin_port);
    return cli_fd;
}

int anet_tcp_connect(const char* ip,int port,int timeout_ms)
{
    int fd = 0;
    struct sockaddr_in  addr;
    fd_set fdr, fdw;
    struct timeval timeout;
    int err = 0;
    int errlen = sizeof(err);

    fd = socket(AF_INET,SOCK_STREAM,0);
    if (fd < 0) {
        fprintf(stderr, "create socket failed,error:%s.\n", strerror(errno));
        return -1;
    }

    memset(&addr,0x00, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    inet_pton(AF_INET, ip, &addr.sin_addr);

    /*设置套接字为非阻塞*/
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags < 0) {
        fprintf(stderr, "Get flags error:%s\n", strerror(errno));
        close(fd);
        return -1;
    }
    flags |= O_NONBLOCK;
    if (fcntl(fd, F_SETFL, flags) < 0) {
        fprintf(stderr, "Set flags error:%s\n", strerror(errno));
        close(fd);
        return -1;
    }

    /*阻塞情况下linux系统默认超时时间为75s*/
    int rc = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
    if (rc != 0) {
        if (errno == EINPROGRESS) {
            if(timeout_ms==0)
                return fd;
            /*正在处理连接*/
            FD_ZERO(&fdr);
            FD_ZERO(&fdw);
            FD_SET(fd, &fdr);
            FD_SET(fd, &fdw);
            if(timeout_ms>0)
            {
                timeout.tv_sec = 0;
                timeout.tv_usec = timeout_ms*1000;
            
                rc = select(fd + 1, &fdr, &fdw, NULL, &timeout);
            }
            else
            {
                rc = select(fd + 1, &fdr, &fdw, NULL, NULL);
            }
            printf("rc is: %d\n", rc);
            /*select调用失败*/
            if (rc < 0) {
                fprintf(stderr, "select error:%s\n", strerror(errno));
                close(fd);
                return -1;
            }

            /*连接超时*/
            if (rc == 0) {
                if(timeout_ms==0)
                   return fd;
		fprintf(stderr, "Connect timeout.\n");
                close(fd);
                return -1;
            }
            /*[1] 当连接成功建立时，描述符变成可写,rc=1*/
            if (rc == 1 && FD_ISSET(fd, &fdw)) {
                printf("Connect success\n");
            }
            /*[2] 当连接建立遇到错误时，描述符变为即可读，也可写，rc=2 遇到这种情况，可调用getsockopt函数*/
            if (rc == 2) {
                if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == -1) {
                    fprintf(stderr, "getsockopt(SO_ERROR): %s", strerror(errno));
                    close(fd);
                    return -1;

                }

                if (err) {
                    errno = err;
                    fprintf(stderr, "connect error:%s\n", strerror(errno));
                    close(fd);
                    return -1;

                }
            }

        }
        else
        {
            fprintf(stderr, "connect failed, error:%s.\n", strerror(errno));
            close(fd);
            return -1;
        }
    }
    return fd;
}

int anet_close(int fd)
{
    if(fd<=0)
        return 0;
    return close(fd);
}


int anet_tcp_read(int fd,void* buf, int sz)
{
    if(buf==NULL||sz<=0)
        return 0;
    int rsz=recv(fd,buf,sz,0);
    if(rsz <= 0)//recv出错
    {
        // EAGAIN 表示读缓冲区暂时没有可读的数据
        if(rsz==-1&&(errno == EAGAIN || errno == EWOULDBLOCK))
            return 0;
        //ECONNRESET 表示收到了对端内核发送的RST信号,表明对端socket已
        //经关闭(网络不通的话，将收不到该信号)
        if(errno==ECONNRESET);//printf("Counter part socket has been closed.\n");
        return -1;
    } 
    return rsz;
}

int anet_tcp_write(int fd,const void* buf, int sz)
{
    int ssz = send(fd, buf, sz, 0);
    return ssz;
}

int anet_tcp_set_nblock(int fd, bool opt)
{
    int flags = fcntl(fd, F_GETFL, 0);//获取文件的flags值。
    
    if(opt)
    {
      
      if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)//设置成非阻塞模式;
      {
        fprintf(stderr, "Set flags error:%s\n", strerror(errno));
        return -1;
      }
      return 0;   
    }
    if (fcntl(fd, F_SETFL,flags&~O_NONBLOCK) < 0) //设置成阻塞模式；
    {
        fprintf(stderr, "Set flags error:%s\n", strerror(errno));
        return -1;
    }
    return 0;
}

int anet_tcp_set_keep_alive(int fd)
{
    return 0;
}
int anet_tcp_set_nodelay(int fd)
{
    return 0;
}
int _anet_set_reuse_addr(int fd)
{
    int opt=1;
    int ret = setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    if (ret) {
        fprintf(stderr, "Reuse addr error:%s\n", strerror(errno));
        return -1;
    }
 
    ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
    if (ret) {
        fprintf(stderr, "Reuse port error:%s\n", strerror(errno));
        return -1;
    }
    return 0;
}






